﻿using System;
using System.Collections.Generic;
using Anderson.NetLobby.MessageContent;
using System.Collections.Concurrent;
using System.Threading;
using System.Net.Sockets;

namespace Anderson.NetLobby.Common
{
    public class NetLobbyRoom : IMessageContainer
    {
        #region Properties
        private INetLobbyLog logger = null;

        private IDictionary<long, NetLobbySession> m_playerSessions;
        private IList<long> m_playerStart;

        private int m_maxPlayerCount;
        private int m_minPlayerCount;
        private long m_roomOwner;
        private bool m_roomStarted = false;
        private long m_roomId = 0;

        public int MinPlayerCount
        {
            get
            {
                return m_minPlayerCount;
            }

            set
            {
                m_minPlayerCount = value;
            }
        }

        public int MaxPlayerCount
        {
            get
            {
                return m_maxPlayerCount;
            }

            set
            {
                m_maxPlayerCount = value;
            }
        }


        public IDictionary<long, NetLobbySession> PlayerSessions
        {
            get
            {
                return m_playerSessions;
            }

            set
            {
                m_playerSessions = value;
            }
        }

        public int PlayerCount
        {
            get
            {
                return m_playerSessions.Count;
            }
        }

        public IList<long> PlayerStart
        {
            get
            {
                return m_playerStart;
            }

            set
            {
                m_playerStart = value;
            }
        }

        public bool RoomStarted
        {
            get
            {
                return m_roomStarted;
            }

            set
            {
                m_roomStarted = value;
            }
        }

        public long RoomOwner
        {
            get
            {
                return m_roomOwner;
            }

            set
            {
                m_roomOwner = value;
            }
        }

        public long RoomId
        {
            get
            {
                return m_roomId;
            }

            set
            {
                m_roomId = value;
            }
        }

        private static long nextRoomId = 0;
        public static long GetNextRoomId()
        {
            return Interlocked.Increment(ref nextRoomId);
        }
        #endregion

        #region Constructor
        public NetLobbyRoom(NetLobbySession session, long id, INetLobbyLog logger)
        {
            this.logger = logger;

            m_playerSessions = new Dictionary<long, NetLobbySession>();
            m_playerStart = new List<long>();

            m_minPlayerCount = NetLobbyDefination.ROOM_MIN_PLAYERCOUNT;
            m_maxPlayerCount = NetLobbyDefination.ROOM_MAX_PLAYERCOUNT;

            m_roomId = id;
            m_roomOwner = session.playerId;

            EnterRoom(session);
        }
        #endregion

        #region Func
        public void EnterRoom(NetLobbySession session)
        {
            if (!m_playerSessions.ContainsKey(session.playerId))
            {
                session.playerInRoom = true;
                session.roomId = m_roomId;
                m_playerSessions.Add(session.playerId, session);
            }
        }

        public void ExitRoom(NetLobbySession session, bool removeSession = true)
        {
            if (m_roomOwner == session.playerId)
            {
                m_roomOwner = 0;
            }

            if (m_playerStart.Contains(session.playerId))
            {
                m_playerStart.Remove(session.playerId);
            }

            if (removeSession && m_playerSessions.ContainsKey(session.playerId))
            {
                m_playerSessions.Remove(session.playerId);
            }
        }

        public void CloseRoom(Action action)
        {
            closeResponseAction = action;

            SendRoomCloseResponse();
        }
        private Action closeResponseAction;

        private void SendRoomCloseResponse()
        {
            MC_RoomAction rc = new MC_RoomAction();
            rc.roomId = m_roomId;

            NetMessage<MC_RoomAction> netMessage = new NetMessage<MC_RoomAction>(
                m_roomId,
                MessageLevelDefinition.CLIENT_L1_ROOM,
                MessageLevelDefinition.CLIENT_L2_ROOM_ACTION,
                MessageLevelDefinition.SERVER_L3_ROOM_CLOSE_RES,
                rc);

            byte[] bytes = MessageSerialize.Serialize(netMessage);

            foreach (NetLobbySession session in m_playerSessions.Values)
            {
                if (null != session.playerSocket && session.playerSocket.Connected)
                {
                    session.playerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, new AsyncCallback(SendRoomCloseResponseCallback), session.playerSocket);
                }
            }

            closeResponseAction();
        }

        private void SendRoomCloseResponseCallback(IAsyncResult ar)
        {
            try
            {
                Socket handler = (Socket)ar.AsyncState;

                handler.EndSend(ar);
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }

        public void RoomPlayerStart(long playerId)
        {
            if (m_playerSessions.ContainsKey(playerId))
            {
                if (!m_playerStart.Contains(playerId))
                {
                    m_playerStart.Add(playerId);
                }
            }
        }
        #endregion

        #region IMessageContainer
        ConcurrentQueue<byte[]> queue = new ConcurrentQueue<byte[]>();
        public void Boardcast()
        {
            //TODO; 考虑将多个消息打包发送
            //但要尽可能将包的大小降低或压缩
            //尽量不传String，改成hash值？

            byte[] bytes = null;
            if (!queue.TryDequeue(out bytes))
            {
                return;
            }

            logger.Log("Room.Boardcast");

            foreach (NetLobbySession session in m_playerSessions.Values)
            {
                if (null != session.playerSocket && session.playerSocket.Connected)
                {
                    session.playerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, new AsyncCallback(SendResponseCallback), session);
                }
            }
        }

        private void SendResponseCallback(IAsyncResult ar)
        {
            try
            {
                NetLobbySession session = (NetLobbySession)ar.AsyncState;
                Socket handler = session.playerSocket;

                handler.EndSend(ar);
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }

        public void Enqueue(byte[] bytes)
        {
            queue.Enqueue(bytes);
        }

        public bool TryDequeue(out byte[] result)
        {
            return queue.TryDequeue(out result);
        }

        internal void RoomAllStart(long playerId, long roomId)
        {
            m_roomStarted = true;

            MC_RoomAction roomAction = new MC_RoomAction();
            roomAction.playerId = playerId;
            roomAction.roomId = roomId;
            roomAction.attachInfo = 1;

            foreach (NetLobbySession session in m_playerSessions.Values)
            {
                if (null != session.playerSocket && session.playerSocket.Connected)
                {
                    SendRoomAllStartResponse(roomAction, session);
                }
            }
        }

        private void SendRoomAllStartResponse(MC_RoomAction roomAction, NetLobbySession session)
        {
            NetMessage<MC_RoomAction> netMessage = new NetMessage<MC_RoomAction>(
                roomAction.roomId,
                MessageLevelDefinition.CLIENT_L1_ROOM,
                MessageLevelDefinition.CLIENT_L2_ROOM_ACTION,
                MessageLevelDefinition.SERVER_L3_ROOM_START_RES,
                roomAction);

            byte[] bytes = MessageSerialize.Serialize(netMessage);

            session.playerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, new AsyncCallback(SendRoomAllStartResponseCallback), session);
        }

        private void SendRoomAllStartResponseCallback(IAsyncResult ar)
        {
            try
            {
                NetLobbySession session = (NetLobbySession)ar.AsyncState;
                Socket handler = session.playerSocket;

                handler.EndSend(ar);
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }
        #endregion

    }
}
